﻿using BalanceManager.Helpers;
using BalanceManager.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using BalanceManager.Services;

namespace BalanceManager.ViewModels
{
    public class HistoryProvider
    {
        private IDataService _dataService;
        private decimal _totalExpensesAmount;
        private decimal _totalProfitsAmount;
        private decimal _maximumExpenseAmount;
        private decimal _maximumProfitAmount;
        private decimal _maximumAmount;

        public HistoryProvider(IDataService dataService)
        {
            if (dataService == null)
                throw new ArgumentException("dataService");

            _dataService = dataService;
        }

        public TreeNode<HistoryItem> HistoryTree(DateTime forDate, Period period)
        {
            var allFundFlows = GetHistoryForPeriod(forDate, period);
            var tree = BuildHistoryTree(_dataService.Items);
            foreach (var child in tree.Children)
            {
                decimal eamount = 0.00M;
                decimal pamount = 0.00M;
                HistoryTreeRecursively(allFundFlows, child, ref eamount, ref pamount);
                eamount += allFundFlows.Sum(i => { if (i.ItemId == child.Item.ItemId) return i.ExpensesAmount; return 0.0M; });
                pamount += allFundFlows.Sum(i => { if (i.ItemId == child.Item.ItemId) return i.ProfitsAmount; return 0.0M; });
                child.Item.ExpensesAmount = eamount;
                child.Item.ProfitsAmount = pamount;
            }
            for (int i = 0; i < tree.Children.Count; ++i)
            {
                if (tree.Children[i].Item.ExpensesAmount == 0 && tree.Children[i].Item.ProfitsAmount == 0)
                {
                    tree.Children.RemoveAt(i);
                    i = -1;
                }
            }
            return tree;
        }

        public decimal TotalExpensesAmount
        {
            get { return _totalExpensesAmount; }
        }

        public decimal TotalProfitsAmount
        {
            get { return _totalProfitsAmount; }
        }

        public decimal MaximumExpenseAmount
        {
            get { return _maximumExpenseAmount; }
        }

        public decimal MaximumProfitAmount
        {
            get { return _maximumProfitAmount; }
        }

        public decimal MaximumAmount
        {
            get { return _maximumAmount; }
        }

        private HistoryItem[] GetHistoryForPeriod(DateTime forDate, Period period)
        {
            DateTime startDate;
            DateTime endDate;
            var result = new List<HistoryItem>();
            _totalExpensesAmount = 0.00M;
            _totalProfitsAmount = 0.00M;
            _maximumExpenseAmount = 0.00M;
            _maximumProfitAmount = 0.00M;
            _maximumAmount = 0.00M;
            DateUtils.GetStartAndEndDateByPeriod(forDate, period, out startDate, out endDate);
            var fundflows = _dataService.LoadFundFlows(startDate, endDate);
            foreach (var f in fundflows)
            {
                var item = _dataService.GetItemById(f.ItemId);
                var hi = new HistoryItem();
                hi.ItemId = f.ItemId;
                hi.ItemName = item.Name;
                hi.ItemParentId = item.ParentId;
                if (f.FlowType == FlowType.Expense)
                {
                    hi.ExpensesAmount = f.Amount;
                    _totalExpensesAmount += f.Amount;
                    if (_maximumExpenseAmount < f.Amount)
                        _maximumExpenseAmount = f.Amount;
                }
                if (f.FlowType == FlowType.Profit)
                {
                    hi.ProfitsAmount = f.Amount;
                    _totalProfitsAmount += f.Amount;
                    if (_maximumProfitAmount < f.Amount)
                        _maximumProfitAmount = f.Amount;
                }
                if (_maximumAmount < f.Amount)
                    _maximumAmount = f.Amount;
                result.Add(hi);
            }

            return result.ToArray();
        }

        private TreeNode<HistoryItem> BuildHistoryTree(List<Item> allItems)
        {
            var root = new TreeNode<HistoryItem>(null);
            var rootNodes = allItems.Where(i => i.ParentId == null);
            foreach (var item in rootNodes)
            {
                var historyItem = new HistoryItem();
                historyItem.ItemId = item.Id;
                historyItem.ItemName = item.Name;
                historyItem.ItemParentId = item.ParentId;
                var treeNode = root.AddChild(historyItem);
                BuildHistoryTreeRecursuvely(allItems, treeNode);
            }
            return root;
        }

        private void BuildHistoryTreeRecursuvely(List<Item> allItems,TreeNode<HistoryItem> root)
        {
            var items = allItems.Where(i => i.ParentId == root.Item.ItemId);
            foreach (var item  in items)
            {
                var historyItem = new HistoryItem();
                historyItem.ItemId = item.Id;
                historyItem.ItemName = item.Name;
                historyItem.ItemParentId = item.ParentId;
                var treeNode = root.AddChild(historyItem);
                BuildHistoryTreeRecursuvely(allItems, treeNode);
            }
        }

        private void HistoryTreeRecursively(HistoryItem[] allFundFlows, TreeNode<HistoryItem> rootNode, ref decimal eamount, ref decimal pamount)
        {
            foreach (var child in rootNode.Children)
            {
                decimal expensesAmount = 0.00M;
                decimal profitsAmount = 0.00M;
                HistoryTreeRecursively(allFundFlows, child, ref eamount, ref pamount);
                expensesAmount += allFundFlows.Sum((i) => { if (i.ItemId == child.Item.ItemId) return i.ExpensesAmount; return 0.0M; });
                profitsAmount += allFundFlows.Sum((i) => { if (i.ItemId == child.Item.ItemId) return i.ProfitsAmount; return 0.0M; });
                child.Item.ExpensesAmount = expensesAmount;
                child.Item.ProfitsAmount = profitsAmount;
                eamount += expensesAmount;
                pamount += profitsAmount;
            }
        }

    }
}